package com.novel2comic.service;

import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis;
import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisParam;
import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.*;
import java.util.List;
import java.util.Base64;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

@Service
public class DashscopeService {
	private static final Logger log = LoggerFactory.getLogger(DashscopeService.class);
	@Value("${novel2comic.dashscopeApiKey:}")
	private String apiKey;
	@Value("${novel2comic.mock:true}")
	private boolean mock;

	private volatile String lastError;
	public String getLastError() { return lastError; }
	
	// 线程池用于并行生成图片
	private final ExecutorService executorService = Executors.newFixedThreadPool(3);

	public Map<String, Object> analyzeToPanels(String text, int target) throws Exception {
		if (mock || apiKey == null || apiKey.isBlank()) {
			return null; // 上层回退示例
		}
		Generation gen = new Generation();
		Message system = Message.builder().role(Role.SYSTEM.getValue())
				.content("你是专业分镜脚本助手，只输出严格 JSON，无解释。" + "()").build();
		String userContent = "请将以下中文小说片段划分为" + target + "个漫画画格。输出 JSON：\n" +
				"{\n  \"panels\": [{\n    \"id\": \"p1\",\n    \"sourceParagraphIds\": [1],\n    \"shot\": \"close_up/medium/wide\",\n    \"angle\": \"eye_level/high/low\",\n    \"setting\": \"详细场景描述，包含时间、地点、环境、光线、色调、质感等\",\n    \"characters\": [{\n      \"name\": \"角色姓名（标注性别）\",\n      \"emotion\": \"情绪状态（如：沉思、平静、坚定、愤怒、悲伤等）\",\n      \"appearance\": \"详细外貌穿着特征（如：长发，白色衬衣，发丝被风吹起，凌乱飘动等）\"\n    }],\n    \"dialog\": [{\n      \"speaker\": \"说话者\",\n      \"text\": \"对话内容，支持中英双语字幕格式\"\n    }]\n  }],\n  \"global\": {\"targetPanelCount\": " + target + "}\n}\n" +
				"规则详解：\n" +
				"1. 镜头景别（shot）：使用 close_up（近景/特写）、medium（中景）、wide（远景），结合构图描述（如：上半身背影、侧身站立、脸部位于画面偏左等）\n" +
				"2. 镜头角度（angle）：eye_level（平视）、high（俯视）、low（仰视），可结合具体描述（如：看向远方）\n" +
				"3. 场景（setting）：详细描述场景，包括：\n" +
				"   - 地点环境（如：海边沙滩、室内、街道等）\n" +
				"   - 时间氛围（如：傍晚、黄昏、夜晚等）\n" +
				"   - 光线效果（如：橙蓝色渐变、胶片质感、柔和漫射光、过度曝光等）\n" +
				"   - 色调质感（如：清冷色调、富士胶片效果、画面粗粝、高光晕化等）\n" +
				"   - 构图信息（如：右侧留白、画面偏左、背景为海面等）\n" +
				"4. 人物（characters）：\n" +
				"   - name：角色姓名，必须标注性别（如：\"女性角色\"、\"男性角色\"、\"少年\"、\"少女\"等）\n" +
				"   - emotion：情绪状态（如：沉思、平静、坚定、温柔、愤怒、悲伤、释然等）\n" +
				"   - appearance：重点描述神态、姿势、朝向、表情（如：遮住半边脸，侧身站立，发丝被海风卷起，眼神直视远方，嘴角浮现浅笑，低头看着手中花束，身体微微前倾等）\n" +
				"   - 必须详细描述人物的神态、姿势、朝向、表情、动作，这些是图像生成的关键\n" +
				"5. 对话（dialog）：\n" +
				"   - speaker：说话者（如：\"女性角色\"、\"字幕\"等）\n" +
				"   - text：对话内容，如需中英双语，格式为：\"中文内容-ENGLISH CONTENT-\"\n" +
				"   - 每格对白≤30字，过短合并相邻内容，过长拆分\n" +
				"6. 保持每张图的一致性，描述要连贯，形成视觉叙事逻辑\n\n" +
				"文本：" + text;
		Message user = Message.builder().role(Role.USER.getValue()).content(userContent).build();
		GenerationParam param = GenerationParam.builder()
				.apiKey(apiKey)
				.model("qwen-plus")
				.messages(Arrays.asList(system, user))
				.resultFormat(GenerationParam.ResultFormat.MESSAGE)
				.temperature(0.2F)
				.build();
		try {
			GenerationResult result = gen.call(param);
			String content = result.getOutput().getChoices().get(0).getMessage().getContent();
			log.info("content:{}", content);
			return Map.of("raw", content);
		} catch (ApiException | NoApiKeyException | InputRequiredException e) {
			lastError = e.getMessage();
			log.error("qwen analyze failed: {}", e.getMessage(), e);
			return null;
		}
	}

	public List<Map<String, String>> generateImages(List<Map<String, Object>> panels, String stylePreset) throws Exception {
		if (mock || apiKey == null || apiKey.isBlank()) {
			return null; // 上层占位图
		}
		
		long startTime = System.currentTimeMillis();
		log.info("Starting parallel generation of {} images", panels.size());
		
		// 限制并发数量为2，避免触发API限流
		final int CONCURRENT_LIMIT = 2;
		List<CompletableFuture<Map<String, String>>> allFutures = new ArrayList<>();
		
		// 分批启动，每批2个并发，批次间延迟1秒
		for (int i = 0; i < panels.size(); i += CONCURRENT_LIMIT) {
			int endIndex = Math.min(i + CONCURRENT_LIMIT, panels.size());
			
			// 创建当前批次的并行任务
			for (int j = i; j < endIndex; j++) {
				Map<String, Object> panel = panels.get(j);
				CompletableFuture<Map<String, String>> future = CompletableFuture.supplyAsync(() -> {
					try {
						return generateSingleImage(panel, stylePreset);
					} catch (Exception e) {
						log.error("Failed to generate image for panel {}: {}", panel.get("id"), e.getMessage(), e);
						return null;
					}
				}, executorService);
				allFutures.add(future);
			}
			
			// 如果不是最后一批，延迟4秒后继续启动下一批
			if (endIndex < panels.size()) {
				log.info("Starting batch for panels {}-{}, waiting 4 seconds before next batch", 
					i + 1, endIndex);
				Thread.sleep(4000);
			}
		}
		
		// 等待所有任务完成
		List<Map<String, String>> results = allFutures.stream()
			.map(CompletableFuture::join)
			.filter(Objects::nonNull)
			.collect(Collectors.toList());
		
		long endTime = System.currentTimeMillis();
		log.info("Generated {} images in {}ms (parallel processing with rate limiting)", 
			results.size(), endTime - startTime);
		
		return results;
	}
	
	private Map<String, String> generateSingleImage(Map<String, Object> panel, String stylePreset) throws Exception {
		String id = String.valueOf(panel.getOrDefault("id", "unknown"));
		String prompt = buildPrompt(panel, stylePreset);
		log.info("Generating image for panel {} with prompt: {}", id, prompt);
		
		long panelStartTime = System.currentTimeMillis();
		
		ImageSynthesis imageSynthesis = new ImageSynthesis();
		Map<String, Object> parameters = new HashMap<>();
		parameters.put("prompt_extend", true);
		parameters.put("watermark", true);

		ImageSynthesisParam param = ImageSynthesisParam.builder()
				.apiKey(apiKey)
				.model("wan2.2-t2i-plus")
				.prompt(prompt)
				.parameters(parameters)
				.n(1)
				.size("1328*1328")
				.build();
		
		try {
			ImageSynthesisResult result = imageSynthesis.call(param);
			
			long panelEndTime = System.currentTimeMillis();
			log.info("Panel {} generated in {}ms", id, panelEndTime - panelStartTime);
			
			if (result == null) {
				log.error("ImageSynthesisResult is null for panel {}", id);
				return null;
			}
			
			if (result.getOutput() == null) {
				log.error("ImageSynthesisOutput is null for panel {}", id);
				return null;
			}
			
			List<?> results = result.getOutput().getResults();
			if (results == null || results.isEmpty()) {
				log.error("ImageSynthesisOutput.getResults() is null or empty for panel {}", id);
				return null;
			}
			
			Object first = results.get(0);
			if (first instanceof Map<?, ?> m) {
				Object u = m.get("url");
				if (u != null) {
					String url = String.valueOf(u);
					log.info("Successfully generated image for panel {}: {}", id, url);
					return Map.of("panelId", id, "url", url);
				} else {
					log.error("URL is null in result for panel {}", id);
					return null;
				}
			} else {
				log.error("First result is not a Map for panel {}, type: {}", id, first != null ? first.getClass() : "null");
				return null;
			}
		} catch (ApiException | NoApiKeyException e) {
			lastError = e.getMessage();
			log.error("qwen-image-plus failed for panel {}: {}", id, e.getMessage(), e);
			throw e;
		} catch (Exception e) {
			lastError = e.getMessage();
			log.error("Unexpected error generating image for panel {}: {}", id, e.getMessage(), e);
			throw e;
		}
	}

	public List<Map<String, String>> generateImagesWithImageEdit(List<Map<String, Object>> panels, String baseImagePath, String stylePreset) throws Exception {
		if (mock || apiKey == null || apiKey.isBlank()) return null;
		List<Map<String, String>> out = new ArrayList<>();
		ImageSynthesis imageSynthesis = new ImageSynthesis();
		String baseAbs = new File(baseImagePath).getAbsolutePath();
		String baseUrl = (baseAbs.startsWith("/") ? "file://" : "file///") + baseAbs.replace("\\", "/");
		String maskDataUrl = makeWhiteMaskDataUrl(1024, 1024);
		Map<String, Object> parameters = new HashMap<>();
		parameters.put("prompt_extend", true);
		for (int i = 0; i < panels.size(); i++) {
			Map<String, Object> p = panels.get(i);
			String id = String.valueOf(p.getOrDefault("id", "p" + (i + 1)));
			String prompt = buildPrompt(p, stylePreset);
			ImageSynthesisParam param = ImageSynthesisParam.builder()
					.apiKey(apiKey)
					.model("wanx2.1-imageedit")
					.function(ImageSynthesis.ImageEditFunction.DESCRIPTION_EDIT_WITH_MASK)
					.prompt(prompt)
					.baseImageUrl(baseUrl)
					.maskImageUrl(maskDataUrl)
					.parameters(parameters)
					.n(1)
					.size("1024*1024")
					.build();
			try {
				ImageSynthesisResult result = imageSynthesis.call(param);
				Object first = result.getOutput().getResults().get(0);
				String url = null;
				if (first instanceof Map<?, ?> m) {
					Object u = m.get("url");
					if (u != null) url = String.valueOf(u);
				}
				if (url != null) out.add(Map.of("panelId", id, "url", url));
			} catch (ApiException | NoApiKeyException e) {
				lastError = e.getMessage();
				log.error("wanxiang imageedit failed for panel {}: {}", id, e.getMessage(), e);
			}
		}
		return out;
	}

	private String makeWhiteMaskDataUrl(int w, int h) throws Exception {
		BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = img.createGraphics();
		g.setComposite(AlphaComposite.Src);
		g.setColor(Color.WHITE);
		g.fillRect(0, 0, w, h);
		g.dispose();
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ImageIO.write(img, "png", bos);
		String b64 = Base64.getEncoder().encodeToString(bos.toByteArray());
		return "data:image/png;base64," + b64;
	}

	private String buildPrompt(Map<String, Object> p, String style) {
		String setting = String.valueOf(p.getOrDefault("setting", ""));
		String shot = String.valueOf(p.getOrDefault("shot", ""));
		String angle = String.valueOf(p.getOrDefault("angle", ""));
		List<Map<String, Object>> chars = (List<Map<String, Object>>) p.getOrDefault("characters", List.of());
		List<Map<String, Object>> dialogs = (List<Map<String, Object>>) p.getOrDefault("dialog", List.of());
		
		StringBuilder sb = new StringBuilder();
		
		// 场景描述（详细）
		sb.append(setting);
		
		// 人物信息（重点强调神态、姿势、朝向、表情）
		if (!chars.isEmpty()) {
			sb.append("，");
			for (int i = 0; i < chars.size(); i++) {
				Map<String, Object> c = chars.get(i);
				String name = String.valueOf(c.getOrDefault("name", "角色"));
				
				// 姓名和情绪放在前面，用括号强调
				sb.append(name);
				if (c.get("emotion") != null) {
					sb.append("（").append(c.get("emotion")).append("）");
				}
				
				// appearance 包含详细的外貌、神态、姿势、朝向、表情描述
				if (c.get("appearance") != null) {
					String appearance = String.valueOf(c.get("appearance"));
					// 重点强调人物神态和姿势细节
					sb.append("，神态姿势：").append(appearance);
				}
				
				if (i < chars.size() - 1) sb.append("、");
			}
		}
		
		// 构图和镜头信息
		sb.append("，构图：").append(shot).append("镜头，角度：").append(angle);
		
		// 添加对话内容
		if (!dialogs.isEmpty()) {
			boolean hasCaptionStyle = false;
			List<String> dialogTexts = new ArrayList<>();
			
			for (Map<String, Object> d : dialogs) {
				String text = String.valueOf(d.getOrDefault("text", ""));
				String speaker = String.valueOf(d.getOrDefault("speaker", ""));
				if (text != null && !text.isEmpty()) {
					// 检查是否为字幕格式（如：包含中英双语或明确标注为"字幕"）
					if (text.contains("-") || "字幕".equals(speaker) || "字幕".equals(text.substring(0, Math.min(2, text.length())))) {
						hasCaptionStyle = true;
					}
					// 包含说话者信息
					String dialogWithSpeaker = speaker != null && !speaker.isEmpty() && !speaker.equals("undefined") 
						? speaker + "：" + text 
						: text;
					dialogTexts.add(dialogWithSpeaker);
				}
			}
			
			if (hasCaptionStyle) {
				// 字幕格式
				sb.append("，字幕：");
				sb.append(String.join(" / ", dialogTexts));
				sb.append("，字幕位于底部居中");
			} else {
				// 对话气泡格式
				sb.append("，对话气泡：");
				for (int i = 0; i < dialogs.size(); i++) {
					Map<String, Object> d = dialogs.get(i);
					String speaker = String.valueOf(d.getOrDefault("speaker", ""));
					String text = String.valueOf(d.getOrDefault("text", ""));
					if (!speaker.isEmpty() && !speaker.equals("undefined")) {
						sb.append(speaker).append("：").append(text);
					} else {
						sb.append(text);
					}
					if (i < dialogs.size() - 1) sb.append(" / ");
				}
			}
		}
		
		// 风格
		sb.append("，风格：").append(expandStyle(style));
		
		return sb.toString();
	}

	private String expandStyle(String preset) {
		Map<String, String> map = new HashMap<>();
		map.put("日式漫画", "日式少年漫画风、墨线清晰、分镜动势强、速度线、网点阴影、暖色调、纸张纹理");
		map.put("美式漫画", "美式漫画风、粗犷线条、强对比、夸张表情、分格明确、鲜艳色块");
		map.put("卡通风格", "卡通手绘、简洁线条、明亮色彩、轻松氛围、可爱比例");
		map.put("写实风格", "真实摄影风格、自然光影、皮肤质感清晰、色彩柔和、光线真实、细节丰富、景深明显、构图平衡、富有情绪张力、还原现实氛围");

		return map.getOrDefault(preset, preset);
	}
}
